package com.oyjd.fw.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

/**
 * FTP处理工具
 * 
 * @author <b>欧建东<br/>
 *         Email:oyjd@163.com</b>
 * 
 */
public class MusicFtpUtil extends TimerTask {
	private static final Log log = LogFactory.getLog(MusicFtpUtil.class);

	private static final int BufferSize = 10240;
	private static String encoding = "GBK";
	private static List<String> useDirs = new ArrayList<String>();
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");

	/**
	 * 同步文件夹
	 * 
	 * @param url
	 *            FTP服务器hostname
	 * @param port
	 *            FTP服务器端口
	 * @param username
	 *            FTP登录账号
	 * @param password
	 *            FTP登录密码
	 * @param remotePath
	 *            FTP服务器上的相对路径，需要以'/'结尾
	 * @param localPath
	 *            下载后保存到本地的路径，需要以'/'结尾
	 * @return
	 */
	public static boolean synDir(String url, int port, String username, String password, String remotePath, String localPath) {
		boolean success = false;
		FTPClient ftp = new FTPClient();
		try {
			int reply;
			ftp.connect(url, port);
			log.info("----------连接到Ftp服务器成功！----------");
			ftp.login(username, password);// 登录
			log.info("----------登录Ftp服务器成功！----------");
			reply = ftp.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				ftp.disconnect();
				return success;
			}
			ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
			ftp.setBufferSize(BufferSize);
			ftp.setControlEncoding(encoding);
			ftp.enterLocalPassiveMode();
			doFold(ftp, remotePath, localPath);
			ftp.logout();
			log.info("----------登出Ftp服务器！----------");
			success = true;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			if (ftp.isConnected()) {
				try {
					ftp.disconnect();
					log.info("----------断开Ftp服务器连接！----------");
				} catch (Exception ioe) {
				}
			}
		}
		return success;
	}

	/**
	 * 迭代文件
	 * 
	 * @param ftp
	 * @param remotePath
	 * @param localPath
	 * @throws IOException
	 */
	private static void doFold(FTPClient ftp, String remotePath, String localPath) throws Exception {
		log.info("----------同步目录:" + remotePath + "--->" + localPath);
		new File(localPath).mkdirs();
		ftp.changeWorkingDirectory(getFtpPath(remotePath));
		FTPFile[] fs = ftp.listFiles(getFtpPath(remotePath));
		List<String> dirs = new ArrayList<String>();
		for (FTPFile f : fs) {
			if (f.isFile()) {
				String path = localPath + f.getName();
				try {
					File ff = new File(path);
					if (ff.exists() && ff.length() == f.getSize()) {
						continue;
					} else {
						ff.delete();
						OutputStream is = new FileOutputStream(ff);
						ftp.retrieveFile(getFtpPath(f.getName()), is);
						is.close();
						bornChk(path);
						log.info("--->完成：" + path);
					}
				} catch (Exception err) {
					log.error(err.getMessage(), err);
				}
			} else if (f.isDirectory() && !f.getName().startsWith(".")) {
				dirs.add(f.getName());
			}
		}
		// 处理目录,由于ftp.changeWorkingDirectory(remotePath);
		// 需要转移工作目录，直接在循环里调用将使后循环的目录错乱
		for (String s : dirs) {
			String souPath = remotePath + s + "/";
			if (doFileter(souPath)) {
				log.info("----------跳过目录:" + souPath);
			} else {
				doFold(ftp, souPath, localPath + s + "/");
			}
		}
	}

	/**
	 * 由于FTP提交路径必须是iso-8859-1 需要转码
	 * 
	 * @param s
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private static String getFtpPath(String s) throws UnsupportedEncodingException {
		return new String(s.getBytes(encoding), "iso-8859-1");
	}

	/**
	 * 目录过滤器
	 * 
	 * @param s
	 *            目标目录
	 * @return
	 */
	private static boolean doFileter(String s) {
		boolean flag = true;
		for (String t : useDirs) {
			if (s.startsWith(t) || t.startsWith(s)) {
				flag = false;
				break;
			}
		}
		return flag;
	}

	/**
	 * 生成校验文件
	 * 
	 * @param name
	 *            文件全路径名
	 * @throws Exception
	 */
	private static void bornChk(String name) throws Exception {
		long size = new File(name).length();
		String chkPath = name + ".zmck";
		new File(chkPath).delete();
		FileOP.writeFile(chkPath, System.currentTimeMillis() + "|" + size, encoding);
	}

	public static void main(String[] args) {
		new MusicFtpUtil().run();
		System.exit(0);
	}

	public static String formatTime(long a) {
		return a / 3600000 + "时" + a / 60000 % 60 + "分" + a / 1000 % 60 + "秒";
	}

	@Override
	public void run() {
		log.info("===============开始同步===============");
		long begin = System.currentTimeMillis();
		try {
			Map<String, String> m = PropUtil.read();
			String musicFtpIP = m.get("musicFtpIP");
			int musicFtpPort = Integer.parseInt(m.get("musicFtpPort"));
			String musicFtpAccount = m.get("musicFtpAccount");
			String musicFtpPwd = m.get("musicFtpPwd");
			String musicSavePath = m.get("musicSavePath");
			String musicDirsReg = m.get("musicDirsReg");
			int musicMonth = Integer.parseInt(m.get("musicMonth"));
			//
			useDirs = new ArrayList<String>();
			Calendar c = Calendar.getInstance();
			String[] regs = musicDirsReg.split(",");
			for (int i = 0; i < musicMonth; i++) {
				String p = sdf.format(c.getTime());
				for (String reg : regs) {
					useDirs.add(MessageFormat.format(reg, p));
				}
				c.add(Calendar.MONTH, -1);
			}
			log.info("----------读取配置文件成功！----------");
			synDir(musicFtpIP, musicFtpPort, musicFtpAccount, musicFtpPwd, "/", musicSavePath);
		} catch (Exception err) {
			log.error(err.getMessage(), err);
		}
		log.info("===============完成同步：共耗时 " + formatTime(System.currentTimeMillis() - begin) + "===============");
	}
}
